"""
Repository模式基础类
"""

from typing import TypeVar, Generic, List, Optional, Dict, Any, Type, Tuple
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, delete, func, text
from sqlalchemy.orm import selectinload, joinedload
from sqlalchemy.sql import Select
from pydantic import BaseModel
import logging
from abc import ABC, abstractmethod

from .database import DatabaseOperation
from ..core.exceptions import NotFoundError, ValidationError, DatabaseError
from ..core.logging import get_logger

logger = get_logger(__name__)

# 泛型类型
ModelType = TypeVar('ModelType')
CreateSchemaType = TypeVar('CreateSchemaType', bound=BaseModel)
UpdateSchemaType = TypeVar('UpdateSchemaType', bound=BaseModel)


class BaseRepository(Generic[ModelType, CreateSchemaType, UpdateSchemaType], ABC):
    """Repository基础类"""
    
    def __init__(self, model: Type[ModelType], session: AsyncSession = None):
        self.model = model
        self.db = DatabaseOperation(session)
        self._session = session
    
    @property
    def session(self) -> AsyncSession:
        """获取数据库会话"""
        if not self._session:
            from .database import get_current_db_session
            session = get_current_db_session()
            if not session:
                raise DatabaseError("No database session available")
            return session
        return self._session
    
    async def create(self, obj_in: CreateSchemaType) -> ModelType:
        """创建记录"""
        try:
            # 转换为模型对象
            db_obj = self.model(**obj_in.dict())
            
            # 保存到数据库
            result = await self.db.insert(db_obj)
            
            logger.info(f"Created {self.model.__name__} with ID: {result.id}")
            return result
            
        except Exception as e:
            logger.error(f"Failed to create {self.model.__name__}: {e}")
            raise DatabaseError(f"Create operation failed: {str(e)}")
    
    async def create_with_attributes(
        self, 
        obj_in: CreateSchemaType,
        **attributes
    ) -> ModelType:
        """创建记录并设置额外属性"""
        try:
            # 转换为字典
            obj_data = obj_in.dict()
            obj_data.update(attributes)
            
            # 创建模型对象
            db_obj = self.model(**obj_data)
            
            # 保存到数据库
            result = await self.db.insert(db_obj)
            
            logger.info(f"Created {self.model.__name__} with ID: {result.id}")
            return result
            
        except Exception as e:
            logger.error(f"Failed to create {self.model.__name__}: {e}")
            raise DatabaseError(f"Create operation failed: {str(e)}")
    
    async def get(self, id: Any) -> Optional[ModelType]:
        """根据ID获取记录"""
        try:
            stmt = select(self.model).where(self.model.id == id)
            result = await self.db.fetch_one(stmt)
            
            if result:
                return result[0] if hasattr(result, '__getitem__') else result
            return None
            
        except Exception as e:
            logger.error(f"Failed to get {self.model.__name__} with ID {id}: {e}")
            raise DatabaseError(f"Get operation failed: {str(e)}")
    
    async def get_by_field(self, field: str, value: Any) -> Optional[ModelType]:
        """根据字段获取记录"""
        try:
            if not hasattr(self.model, field):
                raise ValidationError(f"Field {field} not found in {self.model.__name__}")
            
            stmt = select(self.model).where(getattr(self.model, field) == value)
            result = await self.db.fetch_one(stmt)
            
            if result:
                return result[0] if hasattr(result, '__getitem__') else result
            return None
            
        except ValidationError:
            raise
        except Exception as e:
            logger.error(f"Failed to get {self.model.__name__} by {field}: {e}")
            raise DatabaseError(f"Get by field operation failed: {str(e)}")
    
    async def get_multi(
        self, 
        skip: int = 0, 
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None,
        order_by: Optional[List[str]] = None
    ) -> List[ModelType]:
        """获取多条记录"""
        try:
            stmt = select(self.model)
            
            # 添加过滤条件
            if filters:
                for field, value in filters.items():
                    if hasattr(self.model, field):
                        stmt = stmt.where(getattr(self.model, field) == value)
            
            # 添加排序
            if order_by:
                for field in order_by:
                    desc = field.startswith('-')
                    field_name = field.lstrip('-')
                    
                    if hasattr(self.model, field_name):
                        order_field = getattr(self.model, field_name)
                        if desc:
                            stmt = stmt.order_by(order_field.desc())
                        else:
                            stmt = stmt.order_by(order_field.asc())
            
            # 添加分页
            stmt = stmt.offset(skip).limit(limit)
            
            result = await self.db.fetch_all(stmt)
            
            if result:
                return [item[0] if hasattr(item, '__getitem__') else item for item in result]
            return []
            
        except Exception as e:
            logger.error(f"Failed to get multiple {self.model.__name__}: {e}")
            raise DatabaseError(f"Get multiple operation failed: {str(e)}")
    
    async def update(self, db_obj: ModelType, obj_in: UpdateSchemaType) -> ModelType:
        """更新记录"""
        try:
            # 获取更新数据
            update_data = obj_in.dict(exclude_unset=True)
            
            # 更新对象属性
            for field, value in update_data.items():
                if hasattr(db_obj, field):
                    setattr(db_obj, field, value)
            
            # 保存到数据库
            result = await self.db.update(db_obj)
            
            logger.info(f"Updated {self.model.__name__} with ID: {result.id}")
            return result
            
        except Exception as e:
            logger.error(f"Failed to update {self.model.__name__}: {e}")
            raise DatabaseError(f"Update operation failed: {str(e)}")
    
    async def update_by_id(self, id: Any, obj_in: UpdateSchemaType) -> Optional[ModelType]:
        """根据ID更新记录"""
        try:
            # 获取现有记录
            db_obj = await self.get(id)
            if not db_obj:
                raise NotFoundError(f"{self.model.__name__} with ID {id} not found")
            
            # 更新记录
            return await self.update(db_obj, obj_in)
            
        except (NotFoundError, ValidationError):
            raise
        except Exception as e:
            logger.error(f"Failed to update {self.model.__name__} by ID: {e}")
            raise DatabaseError(f"Update by ID operation failed: {str(e)}")
    
    async def remove(self, id: Any) -> Optional[ModelType]:
        """删除记录"""
        try:
            # 获取现有记录
            db_obj = await self.get(id)
            if not db_obj:
                raise NotFoundError(f"{self.model.__name__} with ID {id} not found")
            
            # 删除记录
            await self.db.delete(db_obj)
            
            logger.info(f"Deleted {self.model.__name__} with ID: {id}")
            return db_obj
            
        except (NotFoundError, ValidationError):
            raise
        except Exception as e:
            logger.error(f"Failed to delete {self.model.__name__}: {e}")
            raise DatabaseError(f"Delete operation failed: {str(e)}")
    
    async def count(self, filters: Optional[Dict[str, Any]] = None) -> int:
        """统计记录数量"""
        try:
            stmt = select(func.count(self.model.id))
            
            # 添加过滤条件
            if filters:
                for field, value in filters.items():
                    if hasattr(self.model, field):
                        stmt = stmt.where(getattr(self.model, field) == value)
            
            result = await self.db.fetch_scalar(stmt)
            return result or 0
            
        except Exception as e:
            logger.error(f"Failed to count {self.model.__name__}: {e}")
            raise DatabaseError(f"Count operation failed: {str(e)}")
    
    async def exists(self, id: Any) -> bool:
        """检查记录是否存在"""
        try:
            stmt = select(self.model).where(self.model.id == id)
            result = await self.db.fetch_one(stmt)
            return result is not None
            
        except Exception as e:
            logger.error(f"Failed to check existence of {self.model.__name__}: {e}")
            return False
    
    async def exists_by_field(self, field: str, value: Any) -> bool:
        """根据字段检查记录是否存在"""
        try:
            if not hasattr(self.model, field):
                return False
            
            stmt = select(self.model).where(getattr(self.model, field) == value)
            result = await self.db.fetch_one(stmt)
            return result is not None
            
        except Exception as e:
            logger.error(f"Failed to check existence by field: {e}")
            return False
    
    async def bulk_create(self, objects_in: List[CreateSchemaType]) -> List[ModelType]:
        """批量创建记录"""
        try:
            # 转换为模型对象
            db_objects = [self.model(**obj.dict()) for obj in objects_in]
            
            # 批量保存
            results = []
            for db_obj in db_objects:
                result = await self.db.insert(db_obj)
                results.append(result)
            
            logger.info(f"Bulk created {len(results)} {self.model.__name__} records")
            return results
            
        except Exception as e:
            logger.error(f"Failed to bulk create {self.model.__name__}: {e}")
            raise DatabaseError(f"Bulk create operation failed: {str(e)}")
    
    async def bulk_update(
        self, 
        updates: List[Tuple[Any, UpdateSchemaType]]
    ) -> List[ModelType]:
        """批量更新记录"""
        try:
            results = []
            for id, obj_in in updates:
                result = await self.update_by_id(id, obj_in)
                if result:
                    results.append(result)
            
            logger.info(f"Bulk updated {len(results)} {self.model.__name__} records")
            return results
            
        except Exception as e:
            logger.error(f"Failed to bulk update {self.model.__name__}: {e}")
            raise DatabaseError(f"Bulk update operation failed: {str(e)}")
    
    async def bulk_delete(self, ids: List[Any]) -> List[ModelType]:
        """批量删除记录"""
        try:
            results = []
            for id in ids:
                result = await self.remove(id)
                if result:
                    results.append(result)
            
            logger.info(f"Bulk deleted {len(results)} {self.model.__name__} records")
            return results
            
        except Exception as e:
            logger.error(f"Failed to bulk delete {self.model.__name__}: {e}")
            raise DatabaseError(f"Bulk delete operation failed: {str(e)}")
    
    async def execute_raw_query(
        self, 
        query: str, 
        params: Optional[Dict[str, Any]] = None
    ) -> Any:
        """执行原生SQL查询"""
        try:
            result = await self.db.execute(text(query), params)
            return result
        except Exception as e:
            logger.error(f"Failed to execute raw query: {e}")
            raise DatabaseError(f"Raw query execution failed: {str(e)}")
    
    async def get_with_relations(
        self, 
        id: Any, 
        relations: Optional[List[str]] = None
    ) -> Optional[ModelType]:
        """获取记录并加载关联关系"""
        try:
            stmt = select(self.model).where(self.model.id == id)
            
            # 添加关联加载
            if relations:
                for relation in relations:
                    if hasattr(self.model, relation):
                        stmt = stmt.options(selectinload(getattr(self.model, relation)))
            
            result = await self.db.fetch_one(stmt)
            
            if result:
                return result[0] if hasattr(result, '__getitem__') else result
            return None
            
        except Exception as e:
            logger.error(f"Failed to get {self.model.__name__} with relations: {e}")
            raise DatabaseError(f"Get with relations operation failed: {str(e)}")
    
    async def paginate(
        self, 
        page: int = 1, 
        per_page: int = 20,
        filters: Optional[Dict[str, Any]] = None,
        order_by: Optional[List[str]] = None
    ) -> Dict[str, Any]:
        """分页查询"""
        try:
            # 计算偏移量
            skip = (page - 1) * per_page
            
            # 获取总数
            total = await self.count(filters)
            
            # 获取数据
            items = await self.get_multi(
                skip=skip,
                limit=per_page,
                filters=filters,
                order_by=order_by
            )
            
            # 计算分页信息
            pages = (total + per_page - 1) // per_page
            
            return {
                "items": items,
                "total": total,
                "page": page,
                "per_page": per_page,
                "pages": pages,
                "has_prev": page > 1,
                "has_next": page < pages,
                "prev_num": page - 1 if page > 1 else None,
                "next_num": page + 1 if page < pages else None
            }
            
        except Exception as e:
            logger.error(f"Failed to paginate {self.model.__name__}: {e}")
            raise DatabaseError(f"Paginate operation failed: {str(e)}")


class CacheableRepository(BaseRepository[ModelType, CreateSchemaType, UpdateSchemaType]):
    """支持缓存的Repository"""
    
    def __init__(self, model: Type[ModelType], session: AsyncSession = None, cache_ttl: int = 3600):
        super().__init__(model, session)
        self.cache_ttl = cache_ttl
        self._cache_client = None
    
    @property
    def cache_client(self):
        """获取缓存客户端"""
        if not self._cache_client:
            from ..services.cache import cache_service
            self._cache_client = cache_service
        return self._cache_client
    
    async def get_cached(self, id: Any) -> Optional[ModelType]:
        """从缓存获取记录"""
        try:
            cache_key = f"{self.model.__name__}:{id}"
            cached_data = await self.cache_client.get(cache_key)
            
            if cached_data:
                # 将缓存数据转换为模型对象
                return self.model(**cached_data)
            
            # 缓存未命中，从数据库获取
            db_obj = await self.get(id)
            if db_obj:
                # 缓存结果
                await self.cache_client.set(
                    cache_key, 
                    db_obj.to_dict() if hasattr(db_obj, 'to_dict') else db_obj.__dict__,
                    expire=self.cache_ttl
                )
            
            return db_obj
            
        except Exception as e:
            logger.error(f"Failed to get cached {self.model.__name__}: {e}")
            # 缓存失败时回退到数据库查询
            return await self.get(id)
    
    async def invalidate_cache(self, id: Any = None, pattern: str = None):
        """使缓存失效"""
        try:
            if id:
                cache_key = f"{self.model.__name__}:{id}"
                await self.cache_client.delete(cache_key)
            elif pattern:
                await self.cache_client.delete_pattern(pattern)
            
        except Exception as e:
            logger.error(f"Failed to invalidate cache: {e}")
